﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using FusionDotNet;
using System.Data;

namespace FusionDotNet.Tests
{
    [TestClass]
    public class BasicTests
    {
        private const string USERNAME = "test@gmail.com";
        private const string PASSWORD = "***";
        private const int TEST_NAMES_TABLE = 377942;
        private const int TEST_TEXT_TABLE = 380760;
        private const int PAUSE_DURATION = 1000;
        private static AuthenticationResult m_AuthResult;

        /// <summary>
        /// Force the system to pause to prevent Google from rejecting our requests.
        /// </summary>
        private void Pause()
        {
            //System.Threading.Thread.Sleep(PAUSE_DURATION);
        }


        [TestCategory("Security"), TestMethod]
        public void CreateValidAuthenticationToken()
        {
            Pause();
            AuthenticationResult result = CreateAuthToken();
            Assert.IsTrue(result.IsAuthenticated);
        }

        private AuthenticationResult CreateAuthToken()
        {
            if (m_AuthResult == null)
            {
                AuthenticationCommand authCommand = new AuthenticationCommand(AccountTypeEnum.Google, USERNAME, PASSWORD, "fusiontables", "visionforms");
                m_AuthResult = authCommand.Execute();
            }
            return m_AuthResult;
        }

        [TestCategory("Security"), TestMethod]
        public void CreateInvalidAuthenticationTokenBadPassword()
        {
            Pause();
            AuthenticationCommand authCommand = new AuthenticationCommand(AccountTypeEnum.Google, USERNAME, "password", "fusiontables", "visionforms");
            AuthenticationResult result = authCommand.Execute();
            Assert.IsTrue(result.IsAuthenticated == false);
        }

        [TestCategory("Security"), TestMethod]
        public void CreateInvalidAuthenticationTokenBadUserName()
        {
            Pause();
            AuthenticationCommand authCommand = new AuthenticationCommand(AccountTypeEnum.Google, "foo", PASSWORD, "fusiontables", "visionforms");
            AuthenticationResult result = authCommand.Execute();
            Assert.IsTrue(result.IsAuthenticated == false);
        }

        [TestCategory("SELECT"), TestMethod]
        public void BasicAnonymousSelect()
        {
            Pause();

            string commandText = "SELECT 'Store Name', Pharmacy, Hours FROM 292905 WHERE Pharmacy='yes' AND ST_INTERSECTS(Address, CIRCLE(LATLNG(37.3242,-121.9806),5000))";
            SelectCommand command = new SelectCommand(commandText);
            SelectResult result = command.Execute();
            Assert.IsTrue(result.ResultTable.Rows.Count > 0);
            
        }

        [TestCategory("SELECT"), TestMethod]
        public void SelectWithBadColumn()
        {
            Pause();

            string commandText = "SELECT ThisColumnMissing FROM 292905";
            SelectCommand command = new SelectCommand(commandText);
            SelectResult result = command.Execute();
            Assert.IsTrue(result.HasExceptions);
            Assert.IsTrue(result.ResultExceptions.OfType<InvalidOperationException>().Count() > 0 );
            System.Diagnostics.Debug.WriteLine(result.ResultExceptions.OfType<InvalidOperationException>().FirstOrDefault().Message);
        }

        [TestCategory("SELECT"), TestMethod]
        public void SelectWithNoQuery()
        {

            try
            {
                SelectCommand command = new SelectCommand(String.Empty);
                Assert.Fail("You cannot pass an empty query.");
            }
            catch (Exception ex)
            {
                Assert.IsInstanceOfType(ex, typeof(ArgumentException));
            }
        }

        [TestCategory("SELECT"), TestMethod]
        public void SelectWithBadTable()
        {
            Pause();
            AuthenticationResult authResult = CreateAuthToken();
            Assert.IsTrue(authResult.IsAuthenticated, "Authentication Failed");

            string commandText = "SELECT * FROM TableThatDoesNotExist";
            SelectCommand command = new SelectCommand(commandText);
            command.AuthenticationToken = authResult.AuthenticationToken;
            SelectResult result = command.Execute();
            Assert.IsTrue(result.HasExceptions);
            Assert.IsTrue(result.ResultExceptions.OfType<InvalidOperationException>().Count() > 0);
            System.Diagnostics.Debug.WriteLine(result.ResultExceptions.OfType<InvalidOperationException>().FirstOrDefault().Message);
        }

        [TestCategory("SELECT"), TestMethod]
        public void BasicAuthenticatedSelect()
        {
            Pause();
            AuthenticationResult authResult = CreateAuthToken();

            Assert.IsTrue(authResult.IsAuthenticated, "Authentication Failed");

            if (authResult.IsAuthenticated)
            {
                string commandText = "SELECT ROWID, FirstName, LastName FROM " + TEST_NAMES_TABLE;
                SelectCommand command = new SelectCommand(commandText);
                command.AuthenticationToken = authResult.AuthenticationToken;
                SelectResult result = command.Execute();
                Assert.IsTrue(result.ResultTable.Rows.Count > 0);
            }
        }

        [TestCategory("SECURITY"), TestMethod]
        public void AttemptToAccessPrivateTableWithNoAuthHeader()
        {
            Pause();
            string commandText = "SELECT * FROM " + TEST_NAMES_TABLE;
            SelectCommand command = new SelectCommand(commandText);
            SelectResult result = command.Execute();
            Assert.IsTrue(result.HasExceptions);
            Assert.IsTrue(result.ResultExceptions.OfType<System.Security.SecurityException>().Count() > 0);
        }

        [TestCategory("INSERT"), TestMethod]
        public void InsertRows()
        {
            Pause();
            AuthenticationResult authResult = CreateAuthToken();

            Assert.IsTrue(authResult.IsAuthenticated, "Authentication Failed");

            DataTable updateTable = new DataTable(TEST_NAMES_TABLE.ToString());
            updateTable.Columns.Add("FirstName");
            updateTable.Columns.Add("LastName");
            updateTable.Rows.Add(new object[] { "Test Update Row 1", DateTime.Now.ToString() });
            updateTable.Rows.Add(new object[] { "Test Update Row 2", DateTime.Now.ToString() });

            InsertCommand command = new InsertCommand(updateTable);
            command.AuthenticationToken = authResult.AuthenticationToken;
            SelectResult result = command.Execute();
            Assert.IsTrue(result.HasExceptions == false);
            Assert.IsTrue(result.ResultTable.Rows.Count == updateTable.Rows.Count);
            Assert.IsTrue(result.ResultTable.Rows[0]["rowid"].ToString() != "");
            Assert.IsTrue(result.ResultTable.Rows[1]["rowid"].ToString() != "");
        }

        [TestMethod]
        public void ListTables()
        {
            Pause();
            AuthenticationResult authResult = CreateAuthToken();

            Assert.IsTrue(authResult.IsAuthenticated, "Authentication Failed");

            ListTablesCommand command = new ListTablesCommand();
            command.AuthenticationToken = authResult.AuthenticationToken;
            ListTablesResult result = command.Execute();
            Assert.IsTrue(result.Tables.Keys.Count > 0); 
        }

        [TestMethod]
        public void GetTableMetaData()
        {
            Pause();
            AuthenticationResult authResult = CreateAuthToken();

            Assert.IsTrue(authResult.IsAuthenticated, "Authentication Failed");

            DescribeTableCommand command = new DescribeTableCommand(TEST_NAMES_TABLE);
            command.AuthenticationToken = authResult.AuthenticationToken;
            SelectResult result = command.Execute();
            Assert.IsTrue(result.ResultTable != null);
            Assert.IsTrue(result.ResultTable.Columns.Count > 0); 
        }

        [TestCategory("UPDATE"), TestMethod]
        public void UpdateRow()
        {
            Pause();
            AuthenticationResult authResult = CreateAuthToken();

            Assert.IsTrue(authResult.IsAuthenticated, "Authentication Failed");

            string commandText = "SELECT ROWID, FirstName, LastName FROM " + TEST_NAMES_TABLE;
            SelectCommand command = new SelectCommand(commandText);
            command.AuthenticationToken = authResult.AuthenticationToken;
            SelectResult result = command.Execute();
            Assert.IsTrue(result.ResultTable.Rows.Count > 0);

            result.ResultTable.TableName = TEST_NAMES_TABLE.ToString();
            result.ResultTable.Rows[0]["LastName"] = DateTime.Now.ToString();

            UpdateCommand updateCommand = new UpdateCommand(result.ResultTable.Rows[0]);
            updateCommand.AuthenticationToken = authResult.AuthenticationToken;
            CommandResult updateResult = updateCommand.Execute();
            Assert.IsTrue(!updateResult.HasExceptions);
            Assert.IsTrue(updateResult.Success);
        }

        [TestCategory("UPDATE"), TestMethod]
        public void UpdateRowNoColumns()
        {
            Pause();
            AuthenticationResult authResult = CreateAuthToken();

            Assert.IsTrue(authResult.IsAuthenticated, "Authentication Failed");

            string commandText = "SELECT ROWID FROM " + TEST_NAMES_TABLE;
            SelectCommand command = new SelectCommand(commandText);
            command.AuthenticationToken = authResult.AuthenticationToken;
            SelectResult result = command.Execute();
            Assert.IsTrue(result.ResultTable.Rows.Count > 0);

            result.ResultTable.TableName = TEST_NAMES_TABLE.ToString();

            try
            {
                UpdateCommand updateCommand = new UpdateCommand(result.ResultTable.Rows[0]);
                updateCommand.AuthenticationToken = authResult.AuthenticationToken;
                CommandResult updateResult = updateCommand.Execute();
            }
            catch (Exception ex)
            {
                Assert.IsInstanceOfType(ex, typeof(InvalidOperationException));
            }
        }


        [TestCategory("DELETE"), TestMethod]
        public void DeleteRow()
        {
            Pause();
            AuthenticationResult authResult = CreateAuthToken();

            Assert.IsTrue(authResult.IsAuthenticated, "Authentication Failed");

            string commandText = "SELECT ROWID, FirstName, LastName FROM " + TEST_NAMES_TABLE;
            SelectCommand command = new SelectCommand(commandText);
            command.AuthenticationToken = authResult.AuthenticationToken;
            SelectResult result = command.Execute();
            Assert.IsTrue(result.ResultTable.Rows.Count > 0);

            result.ResultTable.TableName = TEST_NAMES_TABLE.ToString();

            DeleteCommand deleteCommand = new DeleteCommand(result.ResultTable.Rows[0]);
            deleteCommand.AuthenticationToken = authResult.AuthenticationToken;
            CommandResult deleteResult = deleteCommand.Execute();
            Assert.IsTrue(!deleteResult.HasExceptions);
            Assert.IsTrue(deleteResult.Success);

        }

        [TestCategory("DELETE"), TestMethod]
        public void DeleteTable()
        {
            Pause();
            AuthenticationResult authResult = CreateAuthToken();

            Assert.IsTrue(authResult.IsAuthenticated, "Authentication Failed");

            ListTablesCommand command = new ListTablesCommand();
            command.AuthenticationToken = authResult.AuthenticationToken;
            ListTablesResult result = command.Execute();
            
            Assert.IsTrue(result.Tables.Keys.Count > 0);

            var tableToDelete = result.Tables.Where(i => i.Value.StartsWith("TestTable")).FirstOrDefault();
            Assert.IsTrue(tableToDelete.Key > 0, "No TestTable* was found to delete. Make sure you ran the CreateTable Test first");

            DeleteTableCommand deleteCommand = new DeleteTableCommand(tableToDelete.Key);
            deleteCommand.AuthenticationToken = authResult.AuthenticationToken;
            CommandResult deleteResult = deleteCommand.Execute();
            Assert.IsTrue(deleteResult.HasExceptions == false);
            Assert.IsTrue(deleteResult.Success);
            
        }

        
        public void MergeTable()
        {
            throw new NotImplementedException();
        }

        public void LongPause()
        {
            System.Threading.Thread.Sleep(120000);
        }
    }
}
